{{!

  Copyright (c) Facebook, Inc. and its affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}{{#program:services}}

    pub struct {{service:name}}<'mock> {{>lib/block}}
        {{#service:extends}}
        pub parent: {{service:package}}::mock::{{service:name}}<'mock>,
        {{/service:extends}}
        {{#service:rustFunctions}}{{^function:returns_streams?}}
        pub {{function:name}}: r#impl::{{service:snake}}::{{function:name}}<'mock>,
        {{/function:returns_streams?}}{{/service:rustFunctions}}
        _marker: ::std::marker::PhantomData<&'mock ()>,
    }

    impl dyn super::client::{{service:name}} {
        pub fn mock<'mock>() -> {{service:name}}<'mock> {
            {{service:name}} {
                {{#service:extends}}
                parent: {{service:package}}::client::{{service:name}}::mock(),
                {{/service:extends}}
                {{#service:rustFunctions}}{{^function:returns_streams?}}
                {{function:name}}: {{!
                    }}r#impl::{{service:snake}}::{{function:name}}::unimplemented(),
                {{/function:returns_streams?}}{{/service:rustFunctions}}
                _marker: ::std::marker::PhantomData,
            }
        }
    }

    #[::async_trait::async_trait]
    impl<'mock> super::client::{{service:name}} for {{service:name}}<'mock> {{>lib/block}}{{!
        }}{{#service:rustFunctions}}{{^function:returns_streams?}}
        fn {{function:name}}(
            &self,{{!
            }}{{#function:args}}
            arg_{{field:name}}: {{>lib/arg}},{{!
            }}{{/function:args}}
        ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{!
            }}{{#function:returnType}}{{>lib/type}}{{/function:returnType}}, {{!
            }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{!
        }}>> + ::std::marker::Send + 'static>> {
            let mut closure = self.{{function:name}}.closure.lock().unwrap();
            let closure: &mut dyn ::std::ops::FnMut({{!
                }}{{#function:args}}{{!
                }}{{#field:type}}{{>lib/type}}{{/field:type}}{{!
                }}{{^last?}}, {{/last?}}{{!
                }}{{/function:args}}{{!
            }}) -> _ = &mut **closure;
            ::std::boxed::Box::pin(::futures::future::ready(closure({{!
                }}{{#function:args}}{{!
                }}arg_{{field:name}}{{#field:type}}{{!
                    }}{{#type:string?}}.to_owned(){{/type:string?}}{{!
                    }}{{#type:list?}}.to_owned(){{/type:list?}}{{!
                    }}{{^type:string?}}{{^type:list?}}{{!
                        }}.clone(){{!
                    }}{{/type:list?}}{{/type:string?}}{{!
                }}{{/field:type}}{{!
                }}{{^last?}}, {{/last?}}{{!
                }}{{/function:args}}{{!
            }})))
        }{{!
        }}{{/function:returns_streams?}}{{/service:rustFunctions}}
    }

    {{#service:extendedServices}}
    #[::async_trait::async_trait]
    impl<'mock> {{!
        }}{{#extendedService:service}}{{!
        }}::std::convert::AsRef<dyn {{extendedService:packagePrefix}}::client::{{service:name}} + 'mock> {{!
        }}{{/extendedService:service}}{{!
        }}for {{service:name}}<'mock>
    {
        {{#extendedService:service}}
        fn as_ref(&self) -> &(dyn {{extendedService:packagePrefix}}::client::{{service:name}} + 'mock) {
            self
        }
        {{/extendedService:service}}
    }

    {{/service:extendedServices}}{{!
    }}{{/program:services}}
    mod r#impl {{>lib/block}}{{!
    }}{{#program:services}}
        pub mod {{service:snake}} {{>lib/block}}{{!

            }}{{#service:rustFunctions}}{{^function:returns_streams?}}

            pub struct {{function:name}}<'mock> {
                pub(crate) closure: ::std::sync::Mutex<::std::boxed::Box<
                    dyn ::std::ops::FnMut({{!
                        }}{{#function:args}}{{!
                        }}{{#field:type}}{{>lib/type}}{{/field:type}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}) -> ::std::result::Result<
                        {{#function:returnType}}{{>lib/type}}{{/function:returnType}},
                        {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error,
                    > + ::std::marker::Send + ::std::marker::Sync + 'mock,
                >>,
            }

            impl<'mock> {{function:name}}<'mock> {
                pub fn unimplemented() -> Self {
                    {{function:name}} {
                        closure: ::std::sync::Mutex::new(::std::boxed::Box::new(|{{!
                            }}{{#function:args}}{{!
                            }}_: {{#field:type}}{{>lib/type}}{{/field:type}}{{!
                            }}{{^last?}}, {{/last?}}{{!
                            }}{{/function:args}}{{!
                        }}| panic!(
                            "{}::{} is not mocked",
                            "{{service:name}}",
                            "{{function:name}}",
                        ))),
                    }
                }

                pub fn ret({{!
                    }}&self, {{!
                    }}value: {{#function:returnType}}{{>lib/type}}{{/function:returnType}}{{!
                }}) {
                    self.mock(move |{{!
                        }}{{#function:args}}{{!
                        }}_: {{#field:type}}{{>lib/type}}{{/field:type}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}| value.clone());
                }

                pub fn mock(&self, mut mock: impl ::std::ops::FnMut({{!
                    }}{{#function:args}}{{!
                    }}{{#field:type}}{{>lib/type}}{{/field:type}}{{!
                    }}{{^last?}}, {{/last?}}{{!
                    }}{{/function:args}}{{!
                }}) -> {{#function:returnType}}{{>lib/type}}{{/function:returnType}}{{!
                    }} + ::std::marker::Send + ::std::marker::Sync + 'mock) {{!
                }}{
                    let mut closure = self.closure.lock().unwrap();
                    *closure = ::std::boxed::Box::new(move |{{!
                        }}{{#function:args}}{{!
                        }}{{field:rust_name}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}| ::std::result::Result::Ok(mock({{!
                        }}{{#function:args}}{{!
                        }}{{field:rust_name}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }})));
                }

                pub fn mock_result(&self, mut mock: impl ::std::ops::FnMut({{!
                    }}{{#function:args}}{{!
                    }}{{#field:type}}{{>lib/type}}{{/field:type}}{{!
                    }}{{^last?}}, {{/last?}}{{!
                    }}{{/function:args}}{{!
                }}) -> ::std::result::Result<{{!
                        }}{{#function:returnType}}{{>lib/type}}{{/function:returnType}}, {{!
                        }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error>{{!
                    }} + ::std::marker::Send + ::std::marker::Sync + 'mock) {{!
                }}{
                    let mut closure = self.closure.lock().unwrap();
                    *closure = ::std::boxed::Box::new(move |{{!
                        }}{{#function:args}}{{!
                        }}{{field:rust_name}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}| mock({{!
                        }}{{#function:args}}{{!
                        }}{{field:rust_name}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}));
                }

                pub fn throw<E>(&self, exception: E)
                where
                    E: ::std::convert::Into<{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error>,
                    E: ::std::clone::Clone + ::std::marker::Send + ::std::marker::Sync + 'mock,
                {
                    let mut closure = self.closure.lock().unwrap();
                    *closure = ::std::boxed::Box::new(move |{{!
                        }}{{#function:args}}{{!
                        }}_: {{#field:type}}{{>lib/type}}{{/field:type}}{{!
                        }}{{^last?}}, {{/last?}}{{!
                        }}{{/function:args}}{{!
                    }}| ::std::result::Result::Err(exception.clone().into()));
                }
            }{{!
            }}{{/function:returns_streams?}}{{/service:rustFunctions}}
        }{{!
    }}{{/program:services}}
    }
{{!newline}}
